#include "../include/netdisk.h"
// 全局变量 token
extra_data_t globaltoken;
// 全局变量, 父子线程通信用管道
static int fd[2];
void *threadFunc(void *arg)
{
    train_t *train = (train_t *)arg;
    int ret = 0;
    char ip[128] = {0};
    char port[128] = {0};
    ret = recv(fd[0], ip, sizeof(ip), MSG_WAITALL); // 接收 ip
    if (ret < 0)
    {
        printf("客户端子线程接收ip失败\n");
        close(fd[0]);
        pthread_exit(NULL);
    }
    ret = recv(fd[0], port, sizeof(port), MSG_WAITALL); // 接收 port
    if (ret < 0)
    {
        printf("客户端子线程接收port失败\n");
        close(fd[0]);
        pthread_exit(NULL);
    }
    // printf("子线程收到: ip=%s, port=%s\n", ip, port);
    if (ret < 0)
    {
        printf("客户端子线程read失败\n");
        close(fd[0]);
        pthread_exit(NULL);
    }

    int sockfd = tcpConnect(ip, port);
    if (sockfd < 0)
    {
        printf("客户端子线程连接服务端失败\n");
        close(fd[0]);
        pthread_exit(NULL);
    }

    train_t trainTemp = {0};
    // 文件名
    char filename[64] = {0};
    memcpy(filename, train->data, sizeof(filename));
    // printf("文件名 = |%s|\n", filename);
    // 文件名长度
    // int filelength = train->length;
    // printf("文件名长度 = |%d|\n", filelength);
    // printf("发送前测试: id:|%d|, usrname:|%s|, usrtoken:|%s|\n", globaltoken.cur_dir_id, globaltoken.user, globaltoken.token);
    // 获取要上传的文件大小，文件名，文件hash
    file_mes_t file = {0};
    struct stat statbuf;
    int FD = open(filename, O_RDWR);
    if (FD < 0)
    {
        printf("文件打开失败或文件不存在\n");
    }
    fstat(FD, &statbuf);

    file.file_len = statbuf.st_size;                    // 文件大小
    memcpy(file.file_name, filename, sizeof(filename)); // 文件名
    getFileSha256_str(FD, file.file_hash);              // 文件hash
    // printf("filelen=|%d|, filename=|%s|, filehash=|%s|\n", file.file_len, file.file_name, file.file_hash);
    // 包装火车
    trainTemp.cmdstat = train->cmdstat;
    trainTemp.length = sizeof(globaltoken) + +sizeof(file_mes_t);
    memcpy(trainTemp.data, &globaltoken, sizeof(globaltoken));
    memcpy(trainTemp.data + sizeof(globaltoken), &file, sizeof(file_mes_t));
    // printf("trainTemp.cmdstat=|%d|,trainTemp.length=|%d|\n", trainTemp.cmdstat, trainTemp.length);
    // 发送命令
    send_train(sockfd, &trainTemp);
    printf("子线程: 命令发送成功!\n");
    sleep(1);

    // extra_data_t *newToken = (extra_data_t *)trainTemp.data;
    // printf("发送完测试: id:|%d|, usrname:|%s|, usrtoken:|%s|\n", newToken->cur_dir_id, newToken->user, newToken->token);

    extra_data_t *newToken;
    char *tempdata;
    switch (train->cmdstat)
    {
    case PUTS:
        printf("PUTS命令开始执行......\n");
        memset(&trainTemp, 0, sizeof(train_t));
        recv_train(sockfd, &trainTemp);
        newToken = (extra_data_t *)trainTemp.data;
        memcpy(&globaltoken, newToken, sizeof(extra_data_t));
        // printf("test: id:|%d|, usrname:|%s|, usrtoken:|%s|\n", globaltoken.cur_dir_id, globaltoken.user, globaltoken.token);
        tempdata = trainTemp.data + sizeof(extra_data_t);
        // printf("服务端test: |%s|\n", tempdata);
        if (trainTemp.cmdstat & IS_OK)
        {
            if (trainTemp.cmdstat & SEFIE)
            {
                printf("快传结束......\n");
                break;
            }
            int ret = sendFile_7th(sockfd, file.file_name, 0);
            if (ret == -1)
            {
                printf("PUTS命令执行出错......\n");
            }
            else if (ret == file.file_len)
            {
                printf("上传成功......\n");
            }
            else
            {
                printf("文件为传输中断, 已传ret=%d字节\n", ret);
            }
        }
        printf("PUTS命令执行结束......\n");
        break;
    case GETS:
        printf("GETS命令开始执行......\n");
        memset(&trainTemp, 0, sizeof(train_t));
        recv_train(sockfd, &trainTemp);
        newToken = (extra_data_t *)trainTemp.data;
        memcpy(&globaltoken, newToken, sizeof(extra_data_t));
        printf("test: id:|%d|, usrname:|%s|, usrtoken:|%s|\n", globaltoken.cur_dir_id, globaltoken.user, globaltoken.token);
        tempdata = trainTemp.data + sizeof(extra_data_t);
        memset(&file, 0, sizeof(file_mes_t));
        memcpy(&file, tempdata, sizeof(file_mes_t));
        printf("服务端test: |%s|\n", tempdata);
        if (trainTemp.cmdstat & IS_OK)
        {
            recvFile_7ht(sockfd, file.file_name, file.file_len, 0);
            printf("GETS命令执行成功......\n");
            break;
        }
        printf("GETS命令执行结束......\n");
        break;
    default:
        break;
    }
    close(fd[0]);
    pthread_exit(NULL);
}

int main(int argc, char *argv[])
{
    // ./client ip port
    ARGS_CHECK(argc, 3);
    char ip[128] = {0};
    char port[128] = {0};
    memcpy(ip, argv[1], sizeof(ip));
    memcpy(port, argv[2], sizeof(port));
    // 用户登录
    int sockfd = clientLogin(ip, port);
    if (sockfd == -1)
    {
        printf("登录失败, 服务端断开\n");
        return 0;
    }
    else if (sockfd == 1)
    {
        printf("客户主动断开连接\n");
        return 0;
    }

    int epfd = epoll_create(1);
    train_t train = {0};
    char buf[4096] = {0};

    epollAdd(epfd, STDIN_FILENO); // 监听键盘输入
    epollAdd(epfd, sockfd);       // 监听服务端消息
    struct epoll_event readyset[1024];

    // 登录成功之后，可以执行命令和传输文件
    int commandFlag = 0;
    while (1)
    {
        if (commandFlag == 0)
        {
            printf("登录成功!\n");
            commandFlag = 1;
        }
        int readynum = epoll_wait(epfd, readyset, 1024, -1); // -1永久等待
        for (int i = 0; i < readynum; i++)
        { // 遍历就绪集合
            if (readyset[i].data.fd == STDIN_FILENO)
            {
                memset(buf, 0, sizeof(buf));
                memset(&train, 0, sizeof(train));
                int readcount = read(STDIN_FILENO, buf, sizeof(buf));
                if (readcount == 0)
                {
                    printf("客户退出!\n");
                    goto end;
                }
                buf[strlen(buf) - 1] = '\0';
                // printf("test: buf=%s, readcount=%d\n", buf, readcount);
                /*客户输入命令处理*/
                int ret = commandCheck(buf, &train);
                // printf("test: train.cmd = |%d| train.length = |%d| train.data = |%s|\n", train.cmdstat, train.length, train.data);
                if (ret == -1)
                {
                    printf("请重新输入:\n");
                    break;
                }
                // 长短命令分离, puts/gets交给子线程
                if ((train.cmdstat == PUTS) || (train.cmdstat == GETS))
                {
                    socketpair(AF_LOCAL, SOCK_STREAM, 0, fd); // 全双工通信
                    pthread_t tid;
                    pthread_create(&tid, NULL, threadFunc, &train);
                    send(fd[1], ip, sizeof(ip), MSG_NOSIGNAL);
                    send(fd[1], port, sizeof(port), MSG_NOSIGNAL);
                    close(fd[1]);
                }
                else
                { // 短命令
                    // printf("test: train.cmd = |%d|, train.length = |%d|, train.data = |%s|\n", train.cmdstat, train.length, train.data);
                    // printf("新增用户: id:|%d|, usrname:|%s|, usrtoken:|%s|\n", globaltoken.cur_dir_id, globaltoken.user, globaltoken.token);
                    char filename[64] = {0};
                    int filelength = train.length;
                    // printf("test: train.cmd = |%d|, filelength = |%d|, train.data = |%s|\n", train.cmdstat, filelength, train.data);
                    memcpy(filename, train.data, sizeof(filename));
                    memcpy(train.data, &globaltoken, sizeof(globaltoken));
                    memcpy(train.data + sizeof(globaltoken), filename, sizeof(filename));
                    train.length = sizeof(globaltoken) + filelength;
                    // printf("id = |%d|, usr = |%s|, token = |%s|\n", globaltoken.cur_dir_id, globaltoken.user, globaltoken.token);
                    // printf("test: train.cmd = |%d|, train.length = |%d|, train.data = |%s|\n", train.cmdstat, train.length, train.data);
                    send_train(sockfd, &train);
                    printf("命令发送成功!\n");
                    sleep(1);
                }
            }
            else if (readyset[i].data.fd == sockfd)
            {
                // trainRecv 用来接命令执行后的结果
                train_t trainRecv = {0};
                int is_LS = 0; // 0为直接ls，1为间接ls
                int ret = recv_train(sockfd, &trainRecv);

                if (ret < 0)
                {
                    printf("服务端断开或发生错误...\n");
                    return 0;
                }
                // printf("收到执行命令结果: trainRecv.cmdstat = %d\n", trainRecv.cmdstat);
                switch ((trainRecv.cmdstat & (~IS_OK)) & (~RES))
                // switch (train.cmdstat)
                {
                case CD:
                    printf("CD命令开始执行......\n");
                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    cd_Cmd(sockfd, &trainRecv);
                    printf("CD命令执行完成......\n");

                    break;
                case LS:

                    // printf("LS命令开始执行......\n");

                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    ls_Cmd(&trainRecv);

                    // printf("LS命令执行完成......\n");

                    break;
                case PWD:
                    printf("PWD命令开始执行......\n");
                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    pwd_Cmd(sockfd, &trainRecv);
                    printf("PWD命令执行完成......\n");

                    break;
                case MKDIR:
                    printf("MKDIR命令开始执行......\n");
                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    mkdir_Cmd(sockfd, &trainRecv);
                    printf("MKDIR命令执行完成......\n");

                    break;
                case RMDIR:
                    printf("RMDIR命令开始执行......\n");
                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    rmdir_Cmd(sockfd, &trainRecv);
                    printf("RMDIR命令执行完成......\n");

                    break;
                case REMOVE:
                    printf("REMOVE命令开始执行......\n");
                    if (!(trainRecv.cmdstat & IS_OK))
                    {
                        printf("命令执行失败\n");
                        continue;
                    }
                    remove_Cmd(sockfd, &trainRecv);
                    printf("REMOVE命令执行完成......\n");

                    break;
                default:
                    printf("switch\n");
                    break;
                }
            }
        }
    }

end:
    close(epfd);
    close(sockfd);
    return 0;
}
